/*
 * MyMarqueeHandler.java
 *
 * Created on June 8, 2007, 11:39 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.jerm.Interface.JGraph_Diagrama;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.AbstractAction;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import org.jgraph.JGraph;
import org.jgraph.graph.BasicMarqueeHandler;
import org.jgraph.graph.DefaultPort;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.PortView;

/**
 * MarqueeHandler that Connects Vertices and Displays PopupMenus
 * @author tiagofalcao
 */
public class MyMarqueeHandler extends BasicMarqueeHandler {

        // Holds the Start and the Current Point
        protected Point2D start, current;

        // Holds the First and the Current Port
        protected PortView port, firstPort;
        protected Object target,source;
        
        protected Diagrama_JGraph diagrama;
        
        protected MyGraph graph;
        
        public MyMarqueeHandler(Diagrama_JGraph diagrama){
            this.diagrama=diagrama;
            this.graph=diagrama.graph;
        }

        // Override to Gain Control (for PopupMenu and ConnectMode)
        public boolean isForceMarqueeEvent(MouseEvent e) {
                if (e.isShiftDown())
                        return false;
                // If Right Mouse Button we want to Display the PopupMenu
                if (SwingUtilities.isRightMouseButton(e))
                        // Return Immediately
                        return true;
                // Find and Remember Port
                port = getSourcePortAt(e.getPoint());
                // If Port Found and in ConnectMode (=Ports Visible)
                if (port != null && graph.isPortsVisible())
                        return true;
                // Else Call Superclass
                return super.isForceMarqueeEvent(e);
        }

        // Display PopupMenu or Remember Start Location and First Port
        public void mousePressed(final MouseEvent e) {
                // If Right Mouse Button
                if (SwingUtilities.isRightMouseButton(e)) {
                        // Find Cell in Model Coordinates
                        Object cell = graph.getFirstCellForLocation(e.getX(), e.getY());
                        //Se for Linha com CTRL, quebra
                        //if(cell!=null && cell.getClass()==Aresta.class){
                        //    super.mousePressed(e);
                        //}else{
                            // Create PopupMenu for the Cell
                            JPopupMenu menu = createPopupMenu(e.getPoint(), cell);
                            // Display PopupMenu
                            menu.show(graph, e.getX(), e.getY());
                        //}
                // Else if in ConnectMode and Remembered Port is Valid
                } else if (port != null && graph.isPortsVisible()) {
                        // Remember Start Location
                        start = graph.toScreen(port.getLocation());
                        // Remember the object fro source
                        source=graph.getFirstCellForLocation(e.getX(),e.getY());
                        // Remember First Port
                        firstPort = port;
                } else if(SwingUtilities.isMiddleMouseButton(e)){
                        graph.setScale(1.0);
                }else{
                        // Call Superclass
                        super.mousePressed(e);
                }
        }

        // Find Port under Mouse and Repaint Connector
        public void mouseDragged(MouseEvent e) {
                // If remembered Start Point is Valid
                if (start != null) {
                        // Fetch Graphics from Graph
                        Graphics g = graph.getGraphics();
                        // Reset Remembered Port
                        PortView newPort = getTargetPortAt(e.getPoint());
                        // Do not flicker (repaint only on real changes)
                        if (newPort == null || newPort != port) {
                                // Xor-Paint the old Connector (Hide old Connector)
                                paintConnector(Color.black, graph.getBackground(), g);
                                // If Port was found then Point to Port Location
                                port = newPort;
                                target=graph.getFirstCellForLocation(e.getX(),e.getY());
                                if (port != null)
                                      current = graph.toScreen(port.getLocation());
                                // Else If no Port was found then Point to Mouse Location
                                else
                                        current = graph.snap(e.getPoint());
                                // Xor-Paint the new Connector
                                paintConnector(graph.getBackground(), Color.black, g);
                        }
                }
                // Call Superclass
                super.mouseDragged(e);
        }

        public PortView getSourcePortAt(Point2D point) {
                // Disable jumping
                graph.setJumpToDefaultPort(false);
                PortView result;
                try {
                        // Find a Port View in Model Coordinates and Remember
                        result = graph.getPortViewAt(point.getX(), point.getY());
                } finally {
                        graph.setJumpToDefaultPort(true);
                }
                return result;
        }

        // Find a Cell at point and Return its first Port as a PortView
        protected PortView getTargetPortAt(Point2D point) {
                // Find a Port View in Model Coordinates and Remember
                return graph.getPortViewAt(point.getX(), point.getY());
        }

        // Connect the First Port and the Current Port in the Graph or Repaint
        public void mouseReleased(MouseEvent e) {
                // If Valid Event, Current and First Port
                if (e != null && port != null && firstPort != null
                                && firstPort != port) {
                        // Then Establish Connection
                        diagrama.connect((DefaultPort)firstPort.getCell(),(DefaultPort)port.getCell());
//                        System.out.println(firstPort.getClass());
//                        System.out.println(port.getClass());
//                        System.out.println();
                        e.consume();
                        // Else Repaint the Graph
                } else
                        graph.repaint();
                // Reset Global Vars
                firstPort = port = null;
                start = current = null;
                // Call Superclass
                super.mouseReleased(e);
        }

        // Show Special Cursor if Over Port
        public void mouseMoved(MouseEvent e) {
                // Check Mode and Find Port
                if (e != null && getSourcePortAt(e.getPoint()) != null
                                && graph.isPortsVisible()) {
                        // Set Cusor on Graph (Automatically Reset)
                        graph.setCursor(new Cursor(Cursor.HAND_CURSOR));
                        // Consume Event
                        // Note: This is to signal the BasicGraphUI's
                        // MouseHandle to stop further event processing.
                        e.consume();
                } else
                        // Call Superclass
                        super.mouseMoved(e);
        }

        // Use Xor-Mode on Graphics to Paint Connector
        protected void paintConnector(Color fg, Color bg, Graphics g) {
                // Set Foreground
                g.setColor(fg);
                // Set Xor-Mode Color
                g.setXORMode(bg);
                // Highlight the Current Port
                paintPort(graph.getGraphics());
                // If Valid First Port, Start and Current Point
                if (firstPort != null && start != null && current != null)
                        // Then Draw A Line From Start to Current Point
                        g.drawLine((int) start.getX(), (int) start.getY(),
                                        (int) current.getX(), (int) current.getY());
        }

        // Use the Preview Flag to Draw a Highlighted Port
        protected void paintPort(Graphics g) {
                // If Current Port is Valid
                if (port != null) {
                        // If Not Floating Port...
                        boolean o = (GraphConstants.getOffset(port.getAllAttributes()) != null);
                        // ...Then use Parent's Bounds
                        Rectangle2D r = (o) ? port.getBounds() : port.getParentView()
                                        .getBounds();
                        // Scale from Model to Screen
                        r = graph.toScreen((Rectangle2D) r.clone());
                        // Add Space For the Highlight Border
                        r.setFrame(r.getX() - 3, r.getY() - 3, r.getWidth() + 6, r
                                        .getHeight() + 6);
                        // Paint Port in Preview (=Highlight) Mode
                        graph.getUI().paintCell(g, port, r, true);
                }
        }
    /////////////////////////////////////////////////////////////////////////
    //
    // Classes para o popup
    //
    protected static class EntidadeAction extends AbstractAction{
        Entidade bloco;
        boolean fraca;
        JGraph graph;
        public EntidadeAction(String Name,JGraph graph,Entidade ent){
            super(Name);
            this.bloco=ent;
            this.fraca=! bloco.isFraca();
            this.graph=graph;
        }
        public void actionPerformed(ActionEvent e) {
            bloco.setFraca(fraca);
            graph.getGraphLayoutCache().reload();
        }
    }
    protected static class RelacionamentoAction extends AbstractAction{
        Relacionamento bloco;
        boolean Associativa;
        JGraph graph;
        public RelacionamentoAction(String Name,JGraph graph,Relacionamento rel){
            super(Name);
            this.bloco=rel;
            this.Associativa=!bloco.isAssociativa();
            this.graph=graph;
        }
        public void actionPerformed(ActionEvent e) {
            bloco.setAssociativa(Associativa);
            graph.getGraphLayoutCache().reload();
            
        }
    }
    protected static class ArestaAction extends AbstractAction{
        Aresta edge;
        JGraph graph;
        int min;
        int max;
        public ArestaAction(String Name,JGraph graph,Aresta edge,int min,int max){
            super(Name);
            this.edge=edge;
            this.graph=graph;
            this.min=min;
            this.max=max;
        }
        public void actionPerformed(ActionEvent e) {
            edge.setCardinalidade(min,max);
            graph.getGraphLayoutCache().reload();
            
        }
    }
    protected static class TotalAction extends AbstractAction{
        Aresta edge;
        JGraph graph;
        boolean Total;
        public TotalAction(String Name,JGraph graph,Aresta edge){
            super(Name);
            this.edge=edge;
            this.graph=graph;
            this.Total=! edge.isTotal();
        }
        public void actionPerformed(ActionEvent e) {
            edge.setTotal(Total);
            ((Entidade)((DefaultPort)edge.getTarget()).getUserObject()).setFraca(Total);
            ((Relacionamento)((DefaultPort)edge.getSource()).getUserObject()).setTotal(Total);
            graph.getGraphLayoutCache().reload();
            
        }
    }
    protected static class ArestaStyleAction extends AbstractAction{
        Aresta edge;
        JGraph graph;
        int STYLE;
        public ArestaStyleAction(String Name,JGraph graph,Aresta edge,int STYLE){
            super(Name);
            this.edge=edge;
            this.graph=graph;
            this.STYLE=STYLE;
        }
        public void actionPerformed(ActionEvent e) {
            edge.setStyle(STYLE);
            graph.getGraphLayoutCache().reload();
            
        }
    }
    protected static class HerancaDAction extends AbstractAction{
        Heranca bloco;
        boolean disjunta;
        JGraph graph;
        public HerancaDAction(String Name,JGraph graph,Heranca bloco){
            super(Name);
            this.bloco=bloco;
            this.disjunta=! bloco.isDisjunta();
            this.graph=graph;
        }
        public void actionPerformed(ActionEvent e) {
            bloco.setDisjunta(disjunta);
            graph.getGraphLayoutCache().reload();
            
        }
    }
    protected static class HerancaTAction extends AbstractAction{
        Heranca bloco;
        boolean total;
        JGraph graph;
        public HerancaTAction(String Name,JGraph graph,Heranca bloco){
            super(Name);
            this.bloco=bloco;
            this.total=! bloco.isTotal();
            this.graph=graph;
        }
        public void actionPerformed(ActionEvent e) {
            bloco.setTotal(total);
            graph.getGraphLayoutCache().reload();
        }
    }
    /////////////////////////////////////////////////////////////////////////
    //
    // PopupMenu
    //
    public JPopupMenu createPopupMenu(final Point pt, final Object cell) {
            JPopupMenu menu = new JPopupMenu();
            if (cell != null) {
                    Class<?> classe;
                    classe=cell.getClass();
//                    if(classe == Entidade.class){
//                        Entidade bloco=(Entidade) cell;
//                        if(bloco.isFraca())
//                            menu.add(new EntidadeAction("Nao Fraca",graph,bloco,true));
//                        else
//                            menu.add(new EntidadeAction("Fraca",graph,bloco,false));
//                        menu.addSeparator();
//                    }else 
                        if(classe == Relacionamento.class){
                        Relacionamento bloco=(Relacionamento) cell;
                        if(bloco.isAssociativa())
                            menu.add(new RelacionamentoAction("Relacao",graph,bloco));
                        else if(! bloco.isTotal())
                            menu.add(new RelacionamentoAction("Ent-Associativa",graph,bloco));
                        menu.addSeparator();
                    }else if(classe == Aresta.class){
                        Aresta edge=(Aresta) cell;
                        if(! edge.isHeranca()){
                            Object Target=((DefaultPort)edge.getTarget()).getUserObject();
                            Object Source=((DefaultPort)edge.getSource()).getUserObject();
                            if(Target.getClass()==Entidade.class && Source.getClass()==Relacionamento.class){
                                Relacionamento Source2=(Relacionamento)Source;
                                Entidade Target2=(Entidade)Target;
                                if(edge.isTotal()){
                                    menu.add(new TotalAction("Normal",graph,edge));
                                    menu.addSeparator();
                                }else if((!Source2.isAssociativa()) && (! Source2.isTotal()) && (! Target2.isFraca())){
                                    Object[] edges=graph.getArestas(Target2,Aresta.TARGET);
                                    int i=0;
                                    boolean can=true;
                                    while(i<edges.length){
                                        if(!(((Aresta)edges[i]).isHeranca()) && ((Relacionamento)((DefaultPort)((Aresta)edges[i]).getSource()).getUserObject()).isTotal())
                                            can=false;
                                        i++;
                                    }
                                    if(can && graph.getArestas(Source2,Aresta.SOURCE).length>1){
                                        menu.add(new TotalAction("Total",graph,edge));
                                        menu.addSeparator();  
                                    }
                                }
                            }
                            if(!edge.isTotal()){
                                menu.add(new ArestaAction("(0,1)",graph,edge,0,1));
                                menu.add(new ArestaAction("(1,1)",graph,edge,1,1));
                                menu.add(new ArestaAction("(0,N)",graph,edge,0,2));
                                menu.add(new ArestaAction("(1,N)",graph,edge,1,2));
                                menu.addSeparator();
                            }
//                            menu.add(new ArestaStyleAction("Simples",graph,edge,edge.LINE_SIMPLES));
//                            menu.add(new ArestaStyleAction("Ortog.",graph,edge,edge.LINE_ORTOGONAL));
//                            menu.add(new ArestaStyleAction("Beizer",graph,edge,edge.LINE_BEIZER));
//                            menu.add(new ArestaStyleAction("Spline",graph,edge,edge.LINE_SPLINE));
//                            menu.addSeparator();
                            
                        }
                    }else if(classe==Heranca.class){
                        Heranca bloco=(Heranca) cell;
                        if(bloco.isTotal())
                            menu.add(new HerancaTAction("Parcial",graph,bloco));
                        else
                            menu.add(new HerancaTAction("Total",graph,bloco));
                        if(bloco.isDisjunta())
                            menu.add(new HerancaDAction("OR****",graph,bloco));
                        else
                            menu.add(new HerancaDAction("Disjunta",graph,bloco));
                        menu.addSeparator();
                        
                    }
                    //menu.addSeparator();
                    // Edit
                   // menu.add(new AbstractAction("Trocar") {
                    //        public void actionPerformed(ActionEvent e) {
                            //        graph.startEditingAtCell(cell);
                    //        }
                    //});
                    
                    // Edit
                    if(classe == Entidade.class || classe == Relacionamento.class){
                        menu.add(new AbstractAction("Editar") {
                                public void actionPerformed(ActionEvent e) {
                                        graph.startEditingAtCell(cell);
                                }
                        });
                        menu.addSeparator();
                    }
            }
            // Remove
            if (!graph.isSelectionEmpty()) {
                    menu.add(new AbstractAction("Remover") {
                            public void actionPerformed(ActionEvent e) {
                                diagrama.remove.actionPerformed(null);
                            }
                    });
                    menu.addSeparator();
            }
            
            if (graph.isSelectionEmpty()) {
                // Insert
                menu.add(new AbstractAction("+Entidade") {
                        public void actionPerformed(ActionEvent ev) {
                                diagrama.insertEntidade(pt);
                        }
                });
                // Insert
                menu.add(new AbstractAction("+Relacao") {
                        public void actionPerformed(ActionEvent ev) {
                                diagrama.insertRelacionamento(pt);
                        }
                });
            }
            return menu;
    }

}
